#include "thread.h"
#include <iostream>
#include <assert.h>
#include <hash_map>
#include "criticalsection.h"

namespace gecko
{

	typedef std::hash_map<unsigned int, unsigned int> ThreadIDTable;
	static ThreadIDTable threadIDTable;
	static unsigned int threadsRegistered = 0;
	CriticalSection csThreadTable;

	void ThreadSleep(int milliseconds)
	{
		Sleep(milliseconds);
	}

	void RegisterThread(unsigned int threadID)
	{
		csThreadTable.Begin();
		threadIDTable[threadID] = threadsRegistered++;
		csThreadTable.End();
	}

	void UnRegisterThread(unsigned int threadID)
	{
		csThreadTable.Begin();
		threadIDTable.erase(threadID);
		csThreadTable.End();
	}

	unsigned int GetThreadID()
	{
		return GetCurrentThreadId();
	}

	unsigned int GetThreadIndex()
	{
		return threadIDTable[GetCurrentThreadId()];
	}

	#define MS_VC_EXCEPTION 0x406D1388  

	#pragma pack(push,8)  
	struct THREADNAME_INFO  
	{  
		DWORD	dwType;		// Must be 0x1000.  
		LPCSTR	szName;		// Pointer to name (in user addr space).  
		DWORD	dwThreadID; // Thread ID (-1=caller thread).  
		DWORD	dwFlags;	// Reserved for future use, must be zero.  
	};  
	#pragma pack(pop)  

	void SetThreadName( DWORD dwThreadID, const char* threadName)  
	{  
		THREADNAME_INFO info;  
		info.dwType = 0x1000;  
		info.szName = threadName;  
		info.dwThreadID = dwThreadID;  
		info.dwFlags = 0;  

		__try  
		{  
			RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );  
		}  
		__except(EXCEPTION_EXECUTE_HANDLER)  
		{  
			// printf("Not under debugger. Swallowing exception");  
		}  
	}  

	void ThreadProc(void* param)
	{
		IThread* theThread = static_cast<IThread*>(param);
		theThread->Run();
	}

	IThread::IThread()
	: m_threadHandle(0)
	, m_threadRunning(false)
	{

	}

	IThread::~IThread()
	{
	}

	void IThread::Initialise(const char * threadName)
	{
		m_threadRunning = true;

		m_threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (void*)this, 0, &m_threadID);

		SetThreadName(m_threadID, threadName);
	}

	void IThread::Run()
	{
		RegisterThread(GetThreadID());

		Process();

		UnRegisterThread(GetThreadID());

		CloseHandle(m_threadHandle);

		m_threadRunning = false;
	}

}